package com.yk.system.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yk.api.system.dto.AlarmDTO;
import com.yk.api.system.dto.ConditionDTO;
import com.yk.common.core.constant.CacheConstants;
import com.yk.common.core.constant.NumberConstant;
import com.yk.common.core.domain.BasePageQuery;
import com.yk.common.core.domain.LoginUser;
import com.yk.common.core.domain.PageResult;
import com.yk.common.core.domain.Result;
import com.yk.common.core.utils.LoginHelper;
import com.yk.common.excel.utils.ExcelUtil;
import com.yk.common.log.annotation.Log;
import com.yk.common.log.constant.LogConstants;
import com.yk.common.log.enums.BusinessType;
import com.yk.common.redis.service.RedisService;
import com.yk.system.convert.AlarmConvert;
import com.yk.system.entity.Alarm;
import com.yk.system.entity.Device;
import com.yk.system.entity.Template;
import com.yk.system.entity.Variable;
import com.yk.system.service.AlarmService;
import com.yk.system.service.DeviceService;
import com.yk.system.service.TemplateService;
import com.yk.system.service.VariableService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;


/**
 * 报警规则表 yk-system
 *
 * @author lmx
 * @since 2023-11-21
 */
@Api(tags = "报警规则表")
@RestController
@RequestMapping("/alarm")
@RequiredArgsConstructor
public class AlarmController {

    private final AlarmService alarmService;
    private final AlarmConvert alarmConvert;
    private final DeviceService deviceService;
    private final TemplateService templateService;
    private final VariableService variableService;
    private final RedisService redisService;

    @GetMapping("/getById/{id}")
    @ApiOperation("报警规则表-查询单个")
    public Result<AlarmDTO> getById(@PathVariable(value = "id") Long id) {
        Alarm alarm = alarmService.getById(id);
        if (Objects.isNull(alarm)) {
            return Result.ok();
        }
        AlarmDTO alarmDTO = alarmConvert.entity2Dto(alarm);
        // 触发对象
        if (NumberConstant.ZERO_STR.equals(alarmDTO.getType())) {
            Device device = deviceService.getById(alarmDTO.getTargetId());
            if (Objects.nonNull(device)) {
                alarmDTO.setTargetName(device.getDeviceName());
            }
        } else {
            Template template = templateService.getById(alarmDTO.getTargetId());
            if (Objects.nonNull(template)) {
                alarmDTO.setTargetName(template.getModelName());
            }
        }
        return Result.data(alarmDTO);
    }

    @PostMapping("/save")
    @ApiOperation("报警规则表-新增")
    @Log(title = "报警规则", businessType = BusinessType.INSERT)
    public Result<Void> save(@RequestBody @Validated AlarmDTO dto) {
        alarmService.save(alarmConvert.dto2Entity(dto));
        updateRedis(dto);
        return Result.ok2Log(LogConstants.ADD + dto.getName());
    }

    @PostMapping("/update")
    @ApiOperation("报警规则表-修改")
    @Log(title = "报警规则", businessType = BusinessType.UPDATE)
    public Result<Void> updateById(@RequestBody @Validated AlarmDTO dto) {
        Alarm alarm = alarmService.getById(dto.getId());
        Assert.notNull(alarm, "报警规则信息不存在");
        alarmService.updateById(alarmConvert.dto2Entity(dto));
        updateRedis(dto);
        return Result.ok2Log(LogConstants.UPDATE + alarm.getName());
    }

    @GetMapping("/deleteById/{id}")
    @ApiOperation("报警规则表-删除")
    @Log(title = "报警规则", businessType = BusinessType.DELETE)
    public Result<Boolean> deleteById(@PathVariable(value = "id") Long id) {
        Alarm alarm = alarmService.getById(id);
        if (Objects.isNull(alarm)) {
            return Result.fail("报警规则信息不存在");
        }
        // 缓存清除
        Long targetId = alarm.getTargetId();
        Long variableId = alarm.getVariableId();
        if (redisService.hasKey(CacheConstants.ALARM_KEY + targetId + variableId)) {
            redisService.deleteObject(CacheConstants.ALARM_KEY + targetId + variableId);
        }
        return Result.data2Log(alarmService.removeById(id), LogConstants.DELETE + alarm.getName());
    }

    @PostMapping("/list")
    @ApiOperation("报警规则表-查询列表")
    public Result<List<Alarm>> list(@RequestBody AlarmDTO param) {
        LambdaQueryWrapper<Alarm> lambda = new QueryWrapper<Alarm>().lambda();
        buildCondition(lambda, param);
        return Result.data(alarmService.list(lambda));
    }

    @PostMapping("/page")
    @ApiOperation("报警规则表-分页查询")
    public PageResult<AlarmDTO> page(@RequestBody BasePageQuery<AlarmDTO> pageParam) {
        LambdaQueryWrapper<Alarm> lambda = new QueryWrapper<Alarm>().lambda();
        buildCondition(lambda, pageParam.getParam());
        IPage<AlarmDTO> page = alarmService.page(new Page<>(pageParam.getPageNum(), pageParam.getPageSize()), lambda)
                .convert(alarmConvert::entity2Dto);
        if (!CollUtil.isEmpty(page.getRecords())) {
            page.getRecords().forEach(it -> {
                // 触发对象
                if (NumberConstant.ZERO_STR.equals(it.getType())) {
                    Device device = deviceService.getById(it.getTargetId());
                    if (Objects.nonNull(device)) {
                        it.setTargetName(device.getDeviceName());
                    }
                } else {
                    Template template = templateService.getById(it.getTargetId());
                    if (Objects.nonNull(template)) {
                        it.setTargetName(template.getModelName());
                    }
                }

                // 报警变量
                Variable variable = variableService.getById(it.getVariableId());
                if (Objects.nonNull(variable)) {
                    it.setVariableName(variable.getName());
                }
            });
        }
        return PageResult.success(page.getRecords(), page.getTotal());
    }

    @PostMapping("/selectByAlarm")
    @ApiOperation("根据模板/设备/变量查询规则")
    public Result<List<AlarmDTO>> selectByAlarm(@RequestBody AlarmDTO dto) {
        if (Objects.isNull(dto)) {
            return Result.ok();
        }
        Long targetId = dto.getTargetId();
        Long variableId = dto.getVariableId();
        List<AlarmDTO> cacheDTOList = redisService.getCacheList(CacheConstants.ALARM_KEY + targetId + variableId);
        if (CollUtil.isNotEmpty(cacheDTOList)) {
            return Result.data(cacheDTOList);
        }
        LambdaQueryWrapper<Alarm> lambda = new QueryWrapper<Alarm>().lambda();
        lambda.eq(Alarm::getType, dto.getType());
        lambda.eq(Alarm::getTargetId, targetId);
        lambda.eq(Alarm::getVariableId, variableId);
        lambda.eq(Alarm::getEnable, dto.getEnable());
        List<Alarm> list = alarmService.list(lambda);
        if (CollUtil.isEmpty(list)) {
            return Result.data(CollUtil.newArrayList());
        }
        Variable variable = variableService.getById(variableId);
        List<AlarmDTO> alarmDTOList = list.stream().map(it -> {
            AlarmDTO alarmDTO = alarmConvert.entity2Dto(it);
            if (Objects.nonNull(variable)) {
                alarmDTO.setVariableName(variable.getName());
            }
            return alarmDTO;
        }).collect(Collectors.toList());
        redisService.setCacheList(CacheConstants.ALARM_KEY + targetId + variableId, alarmDTOList);
        return Result.data(alarmDTOList);
    }

    /**
     * 构造查询条件
     *
     * @param lambda 查询条件
     * @param param  查询参数
     */
    private void buildCondition(LambdaQueryWrapper<Alarm> lambda, AlarmDTO param) {
        lambda.eq(Alarm::getCreatedBy, LoginHelper.getLoginUserId());
        if (Objects.nonNull(param)) {
            lambda.eq(Objects.nonNull(param.getTargetId()), Alarm::getTargetId, param.getTargetId());
            lambda.like(StrUtil.isNotEmpty(param.getQ()), Alarm::getName, param.getQ());
        }
        lambda.orderByDesc(Alarm::getCreatedAt);
    }

    /**
     * 更新redis
     *
     * @param dto 实体类
     */
    private void updateRedis(AlarmDTO dto) {
        Alarm alarm = alarmService.getById(dto.getId());
        if (Objects.isNull(alarm)) {
            return;
        }
        Long targetId = alarm.getTargetId();
        Long variableId = alarm.getVariableId();
        if (redisService.hasKey(CacheConstants.ALARM_KEY + targetId + variableId)) {
            redisService.deleteObject(CacheConstants.ALARM_KEY + targetId + variableId);
        }
    }

    @ApiOperation("根据id查询当前数据所在页码")
    @PostMapping("/getPageNum")
    public Result<Integer> getPageNum(@RequestBody BasePageQuery<AlarmDTO> pageParam) {
        AlarmDTO param = pageParam.getParam();
        if (Objects.isNull(param) || Objects.isNull(param.getId())) {
            return Result.data(NumberConstant.ZERO);
        }
        Alarm alarm = alarmService.getById(param.getId());
        if (Objects.isNull(alarm)) {
            return Result.data(NumberConstant.ZERO);
        }
        return Result.data(alarmService.getPageNum(param.getId(), pageParam.getPageSize()));
    }

    @ApiOperation("导出")
    @GetMapping("/export")
    public void export(HttpServletResponse response, AlarmDTO param) {
        if (Objects.isNull(param) || StrUtil.isEmpty(param.getToken())){
            return;
        }
        LoginUser loginUser = LoginHelper.getLoginUser(param.getToken());
        if (Objects.isNull(loginUser)) {
            return;
        }
        LambdaQueryWrapper<Alarm> lambda = new LambdaQueryWrapper<>();
        lambda.eq(Alarm::getCreatedBy, loginUser.getUserId());
        lambda.eq(Objects.nonNull(param.getTargetId()), Alarm::getTargetId, param.getTargetId());
        lambda.like(StrUtil.isNotEmpty(param.getQ()), Alarm::getName, param.getQ());
        lambda.orderByDesc(Alarm::getCreatedAt);
        AtomicInteger index = new AtomicInteger(1);
        List<AlarmDTO> list = Optional.ofNullable(alarmService.list(lambda))
                .orElse(CollUtil.newArrayList())
                .stream()
                .map(it -> {
                    AlarmDTO dto = alarmConvert.entity2Dto(it);
                    // 状态
                    dto.setEnableStr("关闭");
                    if (it.getEnable()) {
                        dto.setEnableStr("启用");
                    }
                    // 触发对象
                    if (NumberConstant.ZERO_STR.equals(it.getType())) {
                        dto.setType("设备触发器");
                        Device device = deviceService.getById(it.getTargetId());
                        if (Objects.nonNull(device)) {
                            dto.setTargetName(device.getDeviceName());
                        }
                    } else {
                        dto.setType("模型触发器");
                        Template template = templateService.getById(it.getTargetId());
                        if (Objects.nonNull(template)) {
                            dto.setTargetName(template.getModelName());
                        }
                    }
                    // 报警变量
                    Variable variable = variableService.getById(it.getVariableId());
                    if (Objects.nonNull(variable)) {
                        dto.setVariableName(variable.getName());
                    }
                    // 触发条件
                    ConditionDTO rule = dto.getRule();
                    if (Objects.nonNull(rule)) {
                        dto.setRuleStr(ConditionDTO.getRuleStr(rule.getName(), rule.getValueA(), rule.getValueB()));
                    }
                    dto.setIndex(index.getAndIncrement());
                    return dto;
                })
                .collect(Collectors.toList());
        ExcelUtil.exportExcel(list, "数据", "报警触发器", AlarmDTO.class, response);
    }
}